﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Serialization;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.ComponentModel
{
    [DataContract]
    public class Model:INotifyPropertyChanged
    {
        private static Dictionary<string, PropertyChangedEventArgs> _EventArgsMap =
            new Dictionary<string, PropertyChangedEventArgs>();

        private PropertyChangedEventHandler _PropertyChangedHandler;
        private SynchronizationContext _SyncContext;

        protected Model()
        {
            this._SyncContext = SynchronizationContext.Current;
        }

        private static PropertyChangedEventArgs GetEventArgs(string propName)
        {
            PropertyChangedEventArgs pe = null;
            if(_EventArgsMap.TryGetValue(propName, out pe)==false)
            {
                pe=new PropertyChangedEventArgs(propName);
                _EventArgsMap[propName] = pe;
            }
            return pe;
        }

        /// <summary>
        /// Raises a change notification event to signal a change in the
        /// specified property's value.
        /// </summary>
        /// <param name="propertyName">The property that has changed.</param>
        protected void RaisePropertyChanged(string propertyName)
        {
            if (String.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }

            if (_PropertyChangedHandler == null)
            {
                return;
            }

            if (_SyncContext != null)
            {
                _SyncContext.Post(delegate(object state)
                {
                    if (_PropertyChangedHandler != null)
                    {
                        _PropertyChangedHandler(this, GetEventArgs(propertyName));
                    }
                }, null);
            }
            else
            {
                _PropertyChangedHandler(this, GetEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Raises a change notification event to signal a change in the
        /// specified properties.
        /// </summary>
        /// <param name="propertyNames">The properties that have changed.</param>
        protected void RaisePropertyChanged(params string[] propertyNames)
        {
            if ((propertyNames == null) || (propertyNames.Length == 0))
            {
                throw new ArgumentNullException("propertyNames");
            }
            if (_PropertyChangedHandler == null)
            {
                return;
            }

            if (_SyncContext != null)
            {
                _SyncContext.Post(delegate(object state)
                {
                    if (_PropertyChangedHandler != null)
                    {
                        foreach (string propertyName in propertyNames)
                        {
                            _PropertyChangedHandler(this, GetEventArgs(propertyName));
                        }
                    }
                }, null);
            }
            else
            {
                foreach (string propertyName in propertyNames)
                {
                    _PropertyChangedHandler(this, GetEventArgs(propertyName));
                }
            }
        }

        #region Implementation of INotifyPropertyChanged

        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add
            {
                _PropertyChangedHandler = (PropertyChangedEventHandler)Delegate.Combine(_PropertyChangedHandler, value);
            }
            remove
            {
                if (_PropertyChangedHandler != null)
                {
                    _PropertyChangedHandler = (PropertyChangedEventHandler)Delegate.Remove(_PropertyChangedHandler, value);
                }
            }
        }

        #endregion
    }
}
